/**
 *
 */
package org.datagear.management.util;

import java.lang.ref.SoftReference;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;

import cn.hutool.core.lang.Pair;

/**
 * LRU缓存池
 *
 * @author jiangmy
 * @date 2012-12-16
 * @version 1.0.0
 */
public class PluralCache<K, V> {
	private final LruMap<K, Pair<V, Long>> cache;

	private final int cacheSeconds;

	/**
	 * 本地缓冲构造函数
	 *
	 * @param maxSize
	 *        最大缓冲量
	 */
	public PluralCache(int maxSize) {
		this.cache = new LruMap<>(maxSize);
		this.cacheSeconds = Integer.MAX_VALUE;
	}

	public PluralCache(int maxSize, int cacheSeconds) {
		this.cache = new LruMap<>(maxSize);
		this.cacheSeconds = cacheSeconds;
	}

	/**
	 * 从本地缓冲获得和key对应的对象
	 *
	 * @param key
	 * @return
	 */
	public V get(K key) {
		Pair<V, Long> pair = this.cache.get(key);
		if (pair == null || System.currentTimeMillis() > pair.getValue()) {
			return null;
		}
		return pair.getKey();
	}

	public Set<K> keySet() {
		return this.cache.keySet();
	}

	/**
	 * 写入一个对象到本地缓冲
	 *
	 * @param key
	 * @param value
	 * @param seconds
	 * @return exists old value
	 */
	public boolean put(K key, V value, long seconds) {
		Pair<V, Long> put = this.cache.put(key, new Pair<>(value, System.currentTimeMillis() + seconds * 1000L));
		return put != null;
	}

	public boolean put(K key, V value, Date expire) {
		if (expire == null) {
			return put(key, value);
		}
		this.cache.put(key, new Pair<>(value, expire.getTime()));
		return get(key) != null;
	}

	public boolean put(K key, V value) {
		return put(key, value, this.cacheSeconds);
	}

	/**
	 * 从本地缓冲中删除和Key值对应的对象并返回
	 *
	 * @param key
	 * @return
	 */
	public boolean remove(K key) {
		this.cache.remove(key);
		return true;
	}

	public boolean containsKey(K key) {
		return get(key) != null;
	}

	/**
	 * 清空缓存
	 *
	 * @return
	 */
	public boolean clear() {
		this.cache.clear();
		return true;
	}

	public int size() {
		return this.cache.size();
	}

	static class LruMap<K, V> {

		private Map<K, SoftReference<V>> cache;

		/**
		 * 构造函数
		 *
		 * @param initCapacity
		 *        int 此map的初始化大小
		 * @param maxSize
		 *        int 此map最大容量，如map插入的数值超过此大小，最早被访问的元素将会被抛弃
		 */
		@SuppressWarnings("serial")
		public LruMap(int initCapacity, final int maxSize) {
			this.cache = Collections.synchronizedMap(new LinkedHashMap<K, SoftReference<V>>(initCapacity, maxSize) {
				@Override
				protected boolean removeEldestEntry(Map.Entry<K, SoftReference<V>> eldest) {
					return size() >= maxSize;
				}
			});
		}

		/**
		 * @param maxSize
		 *        此map的最大容量，初始大小为最大容量的1/4
		 */
		public LruMap(final int maxSize) {
			this(maxSize >> 2, maxSize);
		}

		/**
		 * 写入key和value
		 *
		 * @param key
		 * @param val
		 * @return
		 */
		public V put(K key, V val) {
			SoftReference<V> r = new SoftReference<>(val);
			SoftReference<V> v = this.cache.put(key, r);
			return v == null ? null : v.get();
		}

		/**
		 * 根据key获取value
		 *
		 * @param key
		 * @return
		 */
		public V get(K key) {
			SoftReference<V> r = this.cache.get(key);
			return r == null ? null : r.get();
		}

		/**
		 * 删除缓存中数据
		 *
		 * @param key
		 * @return
		 */
		public V remove(K key) {
			SoftReference<V> r = this.cache.remove(key);
			return r == null ? null : r.get();
		}

		/**
		 * 清除全部数据
		 */
		public void clear() {
			this.cache.clear();
		}

		/**
		 * @return
		 */
		public Set<K> keySet() {
			return this.cache.keySet();
		}

		/**
		 * @return
		 */
		public int size() {
			return this.cache.size();
		}
	}

	/**
	 * cas
	 *
	 * @author jiangmy
	 * @date 2017-05-15 09:33:53
	 * @since v1.0.0
	 * @param key
	 */
	@SuppressWarnings("unchecked")
	public int incrAndGet(K key) {
		int value = 0;
		synchronized (this.cache) {
			V v = get(key);
			if (v != null && !(v instanceof Integer)) {
				throw new RuntimeException("不支持自增!");
			}
			if (v != null) {
				value = (Integer) v;
			}
			value++;
			put(key, (V) Integer.valueOf(value));
		}
		return value;
	}
}
